package com.pic.mgt.manager.business;

import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.dao.DataAccessException;

import com.pic.mgt.dao.IBusinessListMgtDao;
import com.pic.mgt.haishin.HaishinBusinessUnitFactory;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.syushin.SyushinBusinessUnitFactory;
import com.pic.mgt.utils.IgxCollectionUtils;

public abstract class SimpleBusinessManager implements BusinessManager {
	
	protected IBusinessListMgtDao businessListMgtDao;
	protected int businessNameLen;
	protected String syushinBatchTmpPathStr;
	
	protected BusinessManagementListener businessManagementListener;
	
	protected Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanMap;
	
	public SimpleBusinessManager(IBusinessListMgtDao businessListMgtDao,
							     String syushinBatchTmpPathStr,
							     int businessNameLen) throws BusinessManagementOperationException {
		super();
		
		this.businessListMgtDao = businessListMgtDao;
		this.syushinBatchTmpPathStr = syushinBatchTmpPathStr;
		this.businessNameLen = businessNameLen;
		
		basicBusinessInfoBeanMap= new HashMap<String, BasicBusinessInfoBean>();
		
		// 取得第一份有效清單出來
		// throws BusinessManagementOperationException
		getFirstBasicBusinessInfoBeanList();
	}
	
	private void getFirstBasicBusinessInfoBeanList()
							throws BusinessManagementOperationException {
		// 第一份有效清單為,存在於資料庫並且對應的目錄也存在的業務

		// 取得資料庫中所有的業務
		List<BasicBusinessInfoBean> basicBusinessInfoBeanInDbList;
		try {
			basicBusinessInfoBeanInDbList =
				this.businessListMgtDao.findAllBasicBusinessInfo();
		} catch (DataAccessException e) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-254 Building business list failed. Due to database operation error.", e);
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************

				throw e1;
			}
		}

		// 從 Syushin batch tmp 目錄底下取得所有業務目錄名稱 List
		List<String> businessFolderNameList = getBusinessFolderNameListUnderSyushinBucketTmpPath();
		// 將List轉換為Map(Value為null)
		Map<String, Object> businessFolderNameMap = IgxCollectionUtils.covertStringListToMap(businessFolderNameList);

		// 開始產第一份有效清單(如果是位於DB內的業務,有對應的目錄存在,則加入清單)
		for (BasicBusinessInfoBean basicBusinessInfoBeanInDb : basicBusinessInfoBeanInDbList) {
			String businessNameInDb = basicBusinessInfoBeanInDb.getBusinessName();
			if (businessNameInDb.length() != this.businessNameLen) {
				// 若業務ID長度不正確則忽略此端末
				// ********************** Log **********************
				IgxLog.messageLog.error(this,
						"SUDA-255 Building business list: the length of business name '{}' in database is incorrect.",
						businessNameInDb);
				// ********************** Log **********************
				continue;
			}
			if (businessFolderNameMap.containsKey(businessNameInDb)) { // 表示有對應的目錄存在
				// 加入業務清單
				this.basicBusinessInfoBeanMap.put(businessNameInDb, basicBusinessInfoBeanInDb);
			}
		}
	}
	
	@Override
	public synchronized void synchronizeBusiness()
			throws BusinessManagementOperationException {
		/*
		 * (1) 從資料庫中取得所有業務基本資訊的 List
		 * (2) 檢查資料庫中業務名稱的長度和型態是否正確(格式不正確的忽略)
		 * (3) 將業務基本資訊的 List轉換成 Map
		 * (4) 從 Syushin bucket tmp 目錄底下取得所有業務目錄名稱 List
		 * (5) 將業務目錄名稱 List轉換成Map (Value為null)
		 * (6) 比對需要新增的業務並產生之
		 * (7) 比對需要刪除的業務並刪除之
		 */
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Synchronizing business from database.");
		// ********************** Log **********************
		
		synchronized (this.basicBusinessInfoBeanMap) {
			// (1) 從資料庫中取得所有業務基本資訊的 List
			List<BasicBusinessInfoBean> basicBusinessInfoBeanIdDbList;
			try {
				basicBusinessInfoBeanIdDbList =
									businessListMgtDao.findAllBasicBusinessInfo();
			} catch (DataAccessException e) {
				// ********************** Log **********************
				IgxLog.messageDetailsLog.error(this, e.getMessage());
				// ********************** Log **********************
				
				try {
					throw new BusinessManagementOperationException(
							"SUDA-256 Synchronizing business from database failed in database operation.",
							e);
				} catch (BusinessManagementOperationException e1) {
					// ********************** Log **********************
					IgxLog.messageLog.error(this, e1.getMessage());
					// ********************** Log **********************
					
					throw e1;
				}
			}
			
			// (2) 檢長資料庫業務名稱的長度(忽略長度不正確的)
			basicBusinessInfoBeanIdDbList = filterInCorrectBusinessNameLen(
													basicBusinessInfoBeanIdDbList);
			
			// (3) 將業務基本資訊的 List轉換成 Map (Key:業務名稱;Value:BasicBusinessInfoBean)
			Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanInDbMap =
				covertBasicBusinessInfoBeanListToMap(basicBusinessInfoBeanIdDbList);
			
			// (4) 從 Syushin bucket tmp 目錄底下取得所有業務目錄名稱 List
			List<String> businessFolderNameList = getBusinessFolderNameListUnderSyushinBucketTmpPath();
			
			// (5) 將業務目錄名稱 List轉換成Map (Value為null)
			Map<String, Object>  businessFolderNameMap =
				IgxCollectionUtils.covertStringListToMap(businessFolderNameList);
			
			// (6) 比對需要新增的業務並產生之
			synchronizeAddBusiness(businessFolderNameMap, basicBusinessInfoBeanInDbMap);
			
			// (7) 比對需要刪除的業務並刪除之
			synchronizeRemoveBusiness(businessFolderNameMap, basicBusinessInfoBeanInDbMap);
		}
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Synchronizing business from database completed.");
		// ********************** Log **********************
		
		// 通知 Business Management Listener
		this.businessManagementListener.afterSynchronizeBusinessNotify();
	}

	protected abstract void synchronizeAddBusiness(
			Map<String, Object> businessFolderNameMap,
			Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanInDbMap)
			throws BusinessManagementOperationException;
	
	protected abstract void synchronizeRemoveBusiness(
			Map<String, Object> businessFolderNameMap,
			Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanInDbMap)
			throws BusinessManagementOperationException;
	
	private List<String> getBusinessFolderNameListUnderSyushinBucketTmpPath() {
		List<String> businessFolderIdList = new LinkedList<String>();

		// 找出 syshin batch tmp 底下所有的端未目錄(不包含子目錄)
		File[] businessFolderArray = new File(this.syushinBatchTmpPathStr).listFiles();
		
		if (null != businessFolderArray) { // null 表示該目錄內為空的
			// 檢查每個目錄的業務名稱長度是否正確
			synchronized (this.basicBusinessInfoBeanMap) {
				for (File businessFolder : businessFolderArray) {
					if (businessFolder.isDirectory()) {
						String businessName = businessFolder.getName();
						if (businessName.length() == this.businessNameLen) { // 判斷長度是否正確
							businessFolderIdList.add(businessName);
						}
						else {
							// ********************** Log **********************
							if (IgxLog.messageLog.isWarnMode()) {
								IgxLog.messageLog.warn(this,
												"The length of business folder name '{}' under syushin batch tmp is incorrect.",
												businessName);
							}
							// ********************** Log **********************
						}
					}
				}
			}
		}

		return businessFolderIdList;
	}

	private List<BasicBusinessInfoBean> filterInCorrectBusinessNameLen(
			List<BasicBusinessInfoBean> basicBusinessInfoBeanInDbList) {
		List<BasicBusinessInfoBean> filteredList = new LinkedList<BasicBusinessInfoBean>();

		for (BasicBusinessInfoBean basicBusinessInfoBeanInDb : basicBusinessInfoBeanInDbList) {
			String businessName = basicBusinessInfoBeanInDb.getBusinessName();
			String businessType = basicBusinessInfoBeanInDb.getBusinessType();
			// 檢查業務名稱長度是否正確
			if (businessName.length() != this.businessNameLen) {
				// ********************** Log **********************
				IgxLog.messageLog.error(
						this,
						"SUDA-322 Synchronizing business: the length of business name '{}' in database is incorrect.",
						businessName);
				// ********************** Log **********************
				
				continue;
			}
			
			// 檢查業務型態是否正確
			if (!HaishinBusinessUnitFactory.isEffectiveHaishinBusinessType(businessType)&&
				!SyushinBusinessUnitFactory.isEffectiveSyushinBusinessType(businessType)) {
				// ********************** Log **********************
				IgxLog.messageLog.error(
						this,
						"SUDA-323 Synchronizing business: the business '{}' in database has an incorrect type '{}'.",
						businessName, businessType);
				// ********************** Log **********************
				
				continue;
			}
			
			filteredList.add(basicBusinessInfoBeanInDb);
		}

		return filteredList;
	}

	@Override
	public List<BasicBusinessInfoBean> getEffectiveBasicBusinessInfoBeanList() {
		List<BasicBusinessInfoBean> effectiveBasicBusinessInfoBeanlList = new LinkedList<BasicBusinessInfoBean>();

		synchronized(this.basicBusinessInfoBeanMap) {
			Set<String> businessNameSet = this.basicBusinessInfoBeanMap.keySet();
			for (String businessName : businessNameSet) {
				effectiveBasicBusinessInfoBeanlList.add(this.basicBusinessInfoBeanMap.get(businessName));
			}
		}

		return effectiveBasicBusinessInfoBeanlList;
	}
	
	protected Map<String, BasicBusinessInfoBean> covertBasicBusinessInfoBeanListToMap(
			List<BasicBusinessInfoBean> basicBusinessInfoBeanList) {
		Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanMap = new HashMap<String, BasicBusinessInfoBean>();

		for (BasicBusinessInfoBean basicBusinessInfoBean : basicBusinessInfoBeanList) {
			basicBusinessInfoBeanMap.put(basicBusinessInfoBean.getBusinessName(),
										 basicBusinessInfoBean);
		}

		return basicBusinessInfoBeanMap;
	}

	@Override
	public boolean isEffectiveBusiness(String businessName) {
		return
			this.basicBusinessInfoBeanMap.containsKey(businessName);
	}
	
	@Override
	public BasicBusinessInfoBean getBasicBusinessInfoBean(String businessName) {
		return this.basicBusinessInfoBeanMap.get(businessName);
	}
	
	// --- getter and setter ---


	
	public void setBusinessManagementListener(
			BusinessManagementListener businessManagementListener) {
		this.businessManagementListener = businessManagementListener;
	}
	
	@Override
	// (送多方新增)
	public Map<String, BasicBusinessInfoBean> getBasicBusinessInfoBeanMap() {
		return basicBusinessInfoBeanMap;
	}
}
